﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Transactions;
using System.ComponentModel.DataAnnotations;
using SimpleData.Infrastructure.Validation;

namespace SimpleData.Data
{
	public interface IDatabaseScope<out TRepository> : IDisposable
		where TRepository : IRepository
	{
		TRepository Repository
		{ get; }

		void Complete();
	}

	/// <summary>
	/// Representa um escope para realização de tarefas com o banco de dados
	/// </summary>
	/// <typeparam name="TRepository"></typeparam>
	public sealed class DatabaseScope<TRepository> : IDatabaseScope<TRepository> //IDisposable
		where TRepository : IRepository
	{
		private TransactionScope scope;

		public DatabaseScope()
			: this( default( TRepository ) ) /* Resolver a instância do repositório */
		{ }

		public DatabaseScope( TRepository repository )
		{
			this.Repository = repository;
			this.scope = this.CreateScope();
		}

		#region [ Properties ]

		/// <summary>
		/// Obtém o repositório para acessar os dados do BD
		/// </summary>
		public TRepository Repository
		{
			get;
			private set;
		}

		/// <summary>
		/// Indica se uma transação foi finalizada com sucesso
		/// </summary>
		//public bool Completed
		//{
		//    get;
		//    private set;
		//}

		#endregion

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		private TransactionScope CreateScope()
		{
			TimeSpan timeout = TimeSpan.FromMinutes( 20 );

#if DEBUG
			return new TransactionScope( TransactionScopeOption.Required, timeout );
#else
			return new TransactionScope( TransactionScopeOption.Required );
#endif
		}

		/// <summary>
		/// Tenta completar a transação, se houver erros de validação da entidade,
		/// então a transação é cancelado e os erros são retornados
		/// </summary>
		/// <returns></returns>
		public IEnumerable<EntityValidationResult> TryComplete()
		{
			var validation = this.Repository.SaveChanges();

			if ( validation.IsValid() )
				this.scope.Complete();

			return validation;
		}

		/// <summary>
		/// Salva as alterações e completa a transação. Se houver algum erro
		/// de validação, um exceção será lançada
		/// </summary>
		public void Complete()
		{
			var validation = this.TryComplete();

			if ( !validation.IsValid() )
				throw new InvalidOperationException();
		}
		
		/// <summary>
		/// Finaliza a transação
		/// </summary>
		public void Dispose()
		{
			this.scope.Dispose();
		}
	}

}
